package net.xdclass.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import net.xdclass.component.PayFactory;
import net.xdclass.config.RabbitMQConfig;
import net.xdclass.constant.CacheKey;
import net.xdclass.constant.TimeConstant;
import net.xdclass.entity.ProductOrder;
import net.xdclass.entity.ProductOrderItem;
import net.xdclass.enums.*;
import net.xdclass.exception.BizException;
import net.xdclass.feign.CouponFeignService;
import net.xdclass.feign.ProductFeignService;
import net.xdclass.feign.UserFeignService;
import net.xdclass.interceptor.LoginInterceptor;
import net.xdclass.mapper.ProductOrderItemMapper;
import net.xdclass.mapper.ProductOrderMapper;
import net.xdclass.model.LoginUser;
import net.xdclass.model.OrderMessage;
import net.xdclass.request.*;
import net.xdclass.service.IProductOrderService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import net.xdclass.utils.CommonUtil;
import net.xdclass.utils.JsonData;
import net.xdclass.vo.*;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import springfox.documentation.spring.web.json.Json;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author lixin
 * @since 2024-09-03
 */
@Slf4j
@Service
public class ProductOrderServiceImpl extends ServiceImpl<ProductOrderMapper, ProductOrder> implements IProductOrderService {

    @Autowired
    private ProductOrderMapper productOrderMapper;

    @Autowired
    private UserFeignService userFeignService;

    @Autowired
    private ProductFeignService productFeignService;

    @Autowired
    private CouponFeignService couponFeignService;

    @Autowired
    private ProductOrderItemMapper orderItemMapper;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private RabbitMQConfig rabbitMQConfig;

    @Autowired
    private PayFactory payFactory;

    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * * 防重提交
     * * 用户微服务-确认收货地址
     * * 商品微服务-获取最新购物项和价格
     * * 订单验价
     *   * 优惠券微服务-获取优惠券
     *   * 验证价格
     * * 锁定优惠券
     * * 锁定商品库存
     * * 创建订单对象
     * * 创建子订单对象
     * * 发送延迟消息-用于自动关单
     * * 创建支付信息-对接三方支付
     * @param orderRequest
     * @return
     */
    @Transactional
    @Override
    public JsonData confirmOrder(ConfirmOrderRequest orderRequest) {
        //获取登录用户
        LoginUser loginUser = LoginInterceptor.threadLocal.get();

        //获取订单token
        String orderToken = orderRequest.getToken();
        if (StringUtils.isBlank(orderToken)){
            throw new BizException(BizCodeEnum.ORDER_CONFIRM_TOKEN_NOT_EXIST);
        }

        //校验令牌，删除令牌 保持原子性 用lua脚本
        String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";


         Long result = redisTemplate.execute(new DefaultRedisScript<>(script, Long.class), Arrays.asList(String.format(CacheKey.SUBMIT_ORDER_KEY, loginUser.getId())), orderToken);
         if (result == 0){
             throw new BizException(BizCodeEnum.ORDER_CONFIRM_TOKEN_EQUAL_FAIL);
         }

        //订单号
        String orderOutTradeNo = CommonUtil.getStringNumRandom(32);

        //获取用户收货地址
        ProductOrderAddressVO addressVO = this.getUserAddress(orderRequest.getAddressId());

        log.info("收货地址信息:{}", addressVO);

        //获取用户加入购物车的商品
        List<Long> productIdList = orderRequest.getProductIdList();

        ConfirmOrderCartItems confirmOrderCartItems = new ConfirmOrderCartItems();
        confirmOrderCartItems.setOrderOutTradeNo(orderOutTradeNo);
        confirmOrderCartItems.setProductIdList(productIdList);


        JsonData cartItemDate = productFeignService.confirmOrderCartItem(confirmOrderCartItems);
        List<OrderItemVO> orderItemVOList = cartItemDate.getData(new TypeReference<List<OrderItemVO>>() {});
        log.info("获取到的商品 :{}", orderItemVOList);
        if (orderItemVOList == null){
            //购物车商品不存在
            throw new BizException(BizCodeEnum.ORDER_CONFIRM_CART_ITEM_NOT_EXIST);
        }

        //验证价格,减去商品优惠券
        this.checkPrice(orderItemVOList, orderRequest);

        //锁定优惠券
        this.lockCouponRecords(orderRequest, orderOutTradeNo);

        //锁定商品库存
        this.lockProductStocks(orderItemVOList, orderOutTradeNo);

        //创建订单
        ProductOrder productOrder = this.saveProductOrder(orderRequest, loginUser, orderOutTradeNo, addressVO);

        //创建订单项
        this.saveProductOrderItems(orderOutTradeNo, productOrder.getId(), orderItemVOList);

        //发送延迟消息, 用于自动关单
        OrderMessage orderMessage = new OrderMessage();
        orderMessage.setOutTradeNo(orderOutTradeNo);
        rabbitTemplate.convertAndSend(rabbitMQConfig.getEventExchange(), rabbitMQConfig.getOrderCloseDelayRoutingKey(), orderMessage);

        //创建支付
//        payFactory.pay();
        PayInfoVO payInfoVO = new PayInfoVO(productOrder.getOutTradeNo(), productOrder.getPayAmount(), orderRequest.getPayType(), orderRequest.getClientType(), orderItemVOList.get(0).getProductTitle(), "", TimeConstant.ORDER_PAY_TIMEOUT_MILLS);

        String payResult = payFactory.pay(payInfoVO);

        if (StringUtils.isNotBlank(payResult)){
            log.error("创建订单成功,payInfo={}, payResult={}", payInfoVO, payResult);
            return JsonData.buildSuccess(payResult);
        }else {
            log.error("创建订单失败,payInfo={}, payResult={}", payInfoVO, payResult);
            return JsonData.buildResult(BizCodeEnum.PAY_ORDER_FAIL);
        }
    }

    /**
     * 创建订单项
     * @param orderOutTradeNo
     * @param orderId
     * @param orderItemVOList
     */
    private void saveProductOrderItems(String orderOutTradeNo, Long orderId, List<OrderItemVO> orderItemVOList) {

        List<ProductOrderItem> list = orderItemVOList.stream().map(obj -> {
            ProductOrderItem item = new ProductOrderItem();
            item.setBuyNum(obj.getBuyNum());
            item.setProductId(obj.getProductId());
            item.setCreateTime(LocalDateTime.now());
            item.setOutTradeNo(orderOutTradeNo);
            item.setProductImg(obj.getProductImg());
            item.setProductName(obj.getProductTitle());

            //单价
            item.setAmount(obj.getAmount());
            //总价
            item.setTotalAmount(obj.getTotalAmount());
            item.setProductOrderId(orderId);
            return item;
        }).collect(Collectors.toList());

        orderItemMapper.insertBatch(list);
    }

    /**
     * 创建订单
     * @param orderRequest
     * @param loginUser
     * @param orderOutTradeNo
     * @param addressVO
     */
    private ProductOrder saveProductOrder(ConfirmOrderRequest orderRequest, LoginUser loginUser, String orderOutTradeNo, ProductOrderAddressVO addressVO) {

        ProductOrder productOrder = new ProductOrder();
        productOrder.setUserId(loginUser.getId());
        productOrder.setHeadImg(loginUser.getHeadImg());
        productOrder.setNickname(loginUser.getName());

        productOrder.setOutTradeNo(orderOutTradeNo);
        productOrder.setCreateTime(LocalDateTime.now());
        productOrder.setDel(0);
        productOrder.setOrderType(ProductOrderTypeEnum.DAILY.name());

        //实际支付价格
        productOrder.setPayAmount(orderRequest.getRealPayAmount());

        //总价，未使用优惠券的价格
        productOrder.setTotalAmount(orderRequest.getTotalAmount());
        productOrder.setState(ProductOrderStateEnum.NEW.name());
        productOrder.setPayType(ProductOrderPayTypeEnum.valueOf(orderRequest.getPayType()).name());

        productOrder.setReceiverAddress(JSON.toJSONString(addressVO));

        productOrderMapper.insert(productOrder);

        return productOrder;

    }

    /**
     * 锁定商品库存
     * @param orderItemVOList
     * @param orderOutTradeNo
     */
    private void lockProductStocks(List<OrderItemVO> orderItemVOList, String orderOutTradeNo) {

        LockProductRequest lockProductRequest = new LockProductRequest();
        List<OrderItemRequest> requests = orderItemVOList.stream().map(obj -> {
            OrderItemRequest orderItemRequest = new OrderItemRequest();
            orderItemRequest.setProductId(obj.getProductId());
            orderItemRequest.setBuyNum(obj.getBuyNum());
            return orderItemRequest;
        }).collect(Collectors.toList());

        lockProductRequest.setOrderItemList(requests);
        lockProductRequest.setOrderOutTradeNo(orderOutTradeNo);

        //发起锁定商品请求
        JsonData jsonData = productFeignService.lockProducts(lockProductRequest);
        if (jsonData.getCode() != 0){
            throw new BizException(BizCodeEnum.ORDER_CONFIRM_LOCK_PRODUCT_FAIL);
        }
    }

    /**
     * 锁定优惠卷
     * @param orderRequest
     * @param orderOutTradeNo
     */
    private void lockCouponRecords(ConfirmOrderRequest orderRequest, String orderOutTradeNo) {

        List<Long> lockCouponRecordIds = new ArrayList<>();
        if (orderRequest.getCouponRecordId() > 0){
            lockCouponRecordIds.add(orderRequest.getCouponRecordId());

            LockCouponRecordRequest lockCouponRecordRequest = new LockCouponRecordRequest();
            lockCouponRecordRequest.setLockCouponRecordIds(lockCouponRecordIds);
            lockCouponRecordRequest.setOrderOutTradeNo(orderOutTradeNo);

            //发起锁定请求
            JsonData jsonData = couponFeignService.lockCouponRecords(lockCouponRecordRequest);
            if (jsonData.getCode() != 0){
                //锁定失败
                throw new BizException(BizCodeEnum.COUPON_RECORD_LOCK_FAIL);
            }
        }


    }

    /**
     * 验证价格
     * 1.统计全部商品的价格
     * 2.获取优惠券(判断优惠券是否可用),总价减去优惠卷的价格 就是最终价格
     * @param orderItemVOList
     * @param orderRequest
     */
    private void checkPrice(List<OrderItemVO> orderItemVOList, ConfirmOrderRequest orderRequest) {

        //获取商品总价格
        BigDecimal relPayAmount = new BigDecimal("0");
        if (orderItemVOList != null){
            for (OrderItemVO orderitemVO : orderItemVOList) {
                BigDecimal itemRealPayAmount = orderitemVO.getTotalAmount();
                relPayAmount = relPayAmount.add(itemRealPayAmount);
            }
        }

        //获取优惠卷，判断是否可以使用
        CouponRecordVO couponRecordVO = getCartCouponRecord(orderRequest.getCouponRecordId());

        //计算购物车价格，是否满足优惠券满减条件
        if (couponRecordVO != null){
            //计算是否满足，满减条件
            if (relPayAmount.compareTo(couponRecordVO.getConditionPrice()) < 0){
                //不满足
                throw new BizException(BizCodeEnum.ORDER_CONFIRM_COUPON_FAIL);
            }

            //满减金额大于要支付金额
            if (couponRecordVO.getPrice().compareTo(relPayAmount) > 0){
                relPayAmount = BigDecimal.ZERO;
            }else {
                relPayAmount = relPayAmount.subtract(couponRecordVO.getPrice());
            }

            //验证这个价格和前端传过来的价格是否一致
            if (relPayAmount.compareTo(orderRequest.getRealPayAmount()) != 0){
                log.error("订单验证失败 :{}", orderRequest);
                throw new BizException(BizCodeEnum.ORDER_CONFIRM_PRICE_FAIL);
            }
        }

    }

    /**
     * 获取优惠券
     * @param couponRecordId
     * @return
     */
    private CouponRecordVO getCartCouponRecord(Long couponRecordId) {

        if (couponRecordId == null || couponRecordId < 0){
            return null;
        }

        JsonData couponData = couponFeignService.getCouponRecordDetail(couponRecordId);

        if (couponData.getCode() != 0){
            throw new BizException(BizCodeEnum.ORDER_CONFIRM_COUPON_FAIL);
        }

        if (couponData.getCode() == 0){
            CouponRecordVO couponRecordVO = couponData.getData(new TypeReference<CouponRecordVO>() {});
            if (!couponAvailable(couponRecordVO)){
                log.error("优惠券使用失败");
                throw new BizException(BizCodeEnum.COUPON_UNAVAILABLE);
            }
            return couponRecordVO;
        }
        return null;
    }

    /**
     * 判断优惠券是否可用
     * @param couponRecordVO
     * @return
     */
    private boolean couponAvailable(CouponRecordVO couponRecordVO) {

        if (couponRecordVO.getUseState().equalsIgnoreCase(CouponStateEnum.NEW.name())){
            long currentTimestamp = CommonUtil.getCurrentTimestamp();
            long end = couponRecordVO.getEndTime().toInstant(ZoneOffset.of("+8")).toEpochMilli();
            long start = couponRecordVO.getStartTime().toInstant(ZoneOffset.of("+8")).toEpochMilli();
            if (currentTimestamp >= start && currentTimestamp <= end){
                return true;
            }
        }
        return false;
    }

    /**
     * 查询订单状态
     * @param outTradeNo
     * @return
     */
    @Override
    public String queryProductOrderState(String outTradeNo) {
        ProductOrder productOrder = productOrderMapper.selectOne(new QueryWrapper<ProductOrder>().eq("out_trade_no", outTradeNo));

        if (productOrder == null){
            return "";
        }else {
            return productOrder.getState();
        }
    }

    /**
     * 定时关单
     * @param orderMessage
     * @return
     */
    @Override
    public boolean closeProductOrder(OrderMessage orderMessage) {
        ProductOrder productOrder = productOrderMapper.selectOne(new QueryWrapper<ProductOrder>().eq("out_trade_no", orderMessage.getOutTradeNo()));

        if (productOrder == null){
            //订单不存在
            log.warn("确认消息，订单不存在 : {}", orderMessage);
            return true;
        }

        if (productOrder.getState().equalsIgnoreCase(ProductOrderStateEnum.PAY.name())){
            //订单已经支付
            log.info("确认消息,订单已经支付 :{}", orderMessage);
            return true;
        }

        //订单未支付,先向第三方支付平台查询订单支付状态
        PayInfoVO payInfoVO = new PayInfoVO();
        payInfoVO.setPayType(productOrder.getPayType());
        payInfoVO.setOutTradeNo(orderMessage.getOutTradeNo());
        String payResult = payFactory.queryPaySuccess(payInfoVO);

        //结果为空，则未支付，本地取消订单
        if (StringUtils.isBlank(payResult)){
            productOrderMapper.updateOrderPayState(productOrder.getOutTradeNo(), ProductOrderStateEnum.CANCEL.name(), ProductOrderStateEnum.NEW.name());
            log.info("结果为空,则未支付成功，本地取消订单:{}", orderMessage);
            return true;
        }else {
            //支付成功, 主动把订单状态改成已经支付,造成该原因的情况是支付通道的回调有问题
            log.warn("支付成功, 主动把订单状态改成已经支付,造成该原因的情况是支付通道的回调有问题", orderMessage);
            productOrderMapper.updateOrderPayState(productOrder.getOutTradeNo(), ProductOrderStateEnum.PAY.name(), ProductOrderStateEnum.NEW.name());
            return true;
        }
    }

    /**
     * 支付通知结果更新订单状态
     * @param payType
     * @param paramsMap
     * @return
     */
    @Override
    public JsonData handlerOrderCallbackMap(ProductOrderPayTypeEnum payType, Map<String, String> paramsMap) {

        if (payType.name().equalsIgnoreCase(ProductOrderPayTypeEnum.ALIPAY.name())){
            //支付宝支付
            //获取商户订单号
            String outTradeNo = paramsMap.get("out_trade_no");
            //交易状态
            String tradeStatus = paramsMap.get("trade_status");

            if ("TRADE_SUCCESS".equalsIgnoreCase(tradeStatus) || "TRADE_FINISHED".equalsIgnoreCase(tradeStatus)){
                //跟新订单状态
                productOrderMapper.updateOrderPayState(outTradeNo, ProductOrderStateEnum.PAY.name(), ProductOrderStateEnum.NEW.name());
                return JsonData.buildSuccess();
            }

        }else if (payType.name().equalsIgnoreCase(ProductOrderPayTypeEnum.WECHAT.name())) {
            //微信支付 TODO

        }
        return JsonData.buildResult(BizCodeEnum.PAY_ORDER_CALLBACK_SIGN_FAIL);
    }

    /**
     * 分页查询我的订单列表
     * @param page
     * @param size
     * @param state
     * @return
     */
    @Override
    public Map<String, Object> myOrderPage(int page, int size, String state) {
        LoginUser loginUser = LoginInterceptor.threadLocal.get();

        Page<ProductOrder> pageInfo = new Page<>(page, size);

        IPage<ProductOrder> orderIPage = null;
        if (StringUtils.isBlank(state)){
            orderIPage = productOrderMapper.selectPage(pageInfo, new QueryWrapper<ProductOrder>().eq("user_id", loginUser.getId()));
        }else {
            orderIPage = productOrderMapper.selectPage(pageInfo, new QueryWrapper<ProductOrder>().eq("user_id", loginUser.getId()).eq("state", state));
        }

        //获取订单列表
        List<ProductOrder> productOrderList = orderIPage.getRecords();

        List<ProductOrderVO> productOrderVOList = productOrderList.stream()
                .map(order -> {
                    List<ProductOrderItem> itemList = orderItemMapper.selectList(new QueryWrapper<ProductOrderItem>().eq("product_order_id", order.getId()));

                    List<OrderItemVO> itemVOList = itemList.stream().map(item -> {
                        OrderItemVO itemVO = new OrderItemVO();
                        BeanUtils.copyProperties(item, itemVO);
                        return itemVO;
                    }).collect(Collectors.toList());

                    ProductOrderVO productOrderVO = new ProductOrderVO();
                    BeanUtils.copyProperties(order, productOrderVO);
                    productOrderVO.setOrderItemVOList(itemVOList);
                    return productOrderVO;

                }).collect(Collectors.toList());

        Map<String, Object> pageMap = new HashMap<>();
        pageMap.put("total_record", orderIPage.getTotal());
        pageMap.put("total_page", orderIPage.getPages());
        pageMap.put("current_data", productOrderVOList);
        return pageMap;
    }

    /**
     * 订单二次支付
     * @param repayOrderRequest
     * @return
     */
    @Transactional
    @Override
    public JsonData repay(RepayOrderRequest repayOrderRequest) {
        LoginUser loginUser = LoginInterceptor.threadLocal.get();

        ProductOrder productOrder = productOrderMapper.selectOne(new QueryWrapper<ProductOrder>().eq("out_trade_no", repayOrderRequest.getOutTradeNo()).eq("user_id", loginUser.getId()));
        log.info("订单状态: {}", productOrder);

        if (productOrder == null){
            return JsonData.buildResult(BizCodeEnum.PAY_ORDER_NOT_EXIST);
        }

        if (!productOrder.getState().equalsIgnoreCase(ProductOrderStateEnum.NEW.name())){
            return JsonData.buildResult(BizCodeEnum.PAY_ORDER_STATE_ERROR);
        }else {
            //订单状态为NEW, 获取订单创建到现在的存活时间
            long orderLiveTime = CommonUtil.getCurrentTimestamp() - (productOrder.getCreateTime().toInstant(ZoneOffset.of("+8")).toEpochMilli());
            //创建订单是临建点, 所以在增加70秒， 假如29分,则也不能支付
            orderLiveTime = orderLiveTime + 70 * 1000;

            if (orderLiveTime > TimeConstant.ORDER_PAY_TIMEOUT_MILLS){
                //大于订单超时时间,则失效
                return JsonData.buildResult(BizCodeEnum.PAY_ORDER_PAY_TIMEOUT);
            }else {

                //总时间 - 存活时间 = 剩下的有效时间
                long timeout = TimeConstant.ORDER_PAY_TIMEOUT_MILLS - orderLiveTime;
                PayInfoVO payInfoVO = new PayInfoVO(productOrder.getOutTradeNo(), productOrder.getPayAmount(), repayOrderRequest.getPayType(), repayOrderRequest.getClientType(), productOrder.getOutTradeNo(), "", timeout);

                log.info("payInfoVO={}", payInfoVO);

                String payResult = payFactory.pay(payInfoVO);

                if (StringUtils.isNotBlank(payResult)){
                    log.error("创建二次支付订单成功,payInfo={}, payResult={}", payInfoVO, payResult);
                    return JsonData.buildSuccess(payResult);
                }else {
                    log.error("创建二次支付订单失败,payInfo={}, payResult={}", payInfoVO, payResult);
                    return JsonData.buildResult(BizCodeEnum.PAY_ORDER_FAIL);
                }

            }
        }
    }

    /**
     * 获取收货地址详情
     * @param addressId
     * @return
     */
    private ProductOrderAddressVO getUserAddress(long addressId) {
        JsonData addressData = userFeignService.detail(addressId);

        if (addressData.getCode() != 0){
            //获取收货地址失败
            log.info("错误信息: {}", addressData);
            log.error("获取收货地址失败");
            throw new BizException(BizCodeEnum.ADDRESS_NO_EXITS);
        }

        ProductOrderAddressVO addressVO = addressData.getData(new TypeReference<ProductOrderAddressVO>(){});
        return addressVO;
    }
}
